namespace How_toEval;

public interface IBuiltin
{
    static Lazy<IBuiltin> BuiltinInstance { get => throw new Exception("Builtin function prototype cannot be spawned"); }
    int ParametersNumber { get; }
    delegate object Handler(object[] args);
    Handler BuiltinHandler { get; }
}

public class Builtins
{
    public static Memory BuiltinMemory
    {
        get => new Memory(builtinDict);
    }

    public static HashSet<string> BuiltinScope { get => builtinDict.Keys.ToHashSet(); }

    private static Dictionary<string, object> builtinDict = new Dictionary<string, object>{
        {"print", Print.BuiltinInstance},
        {"input", Input.BuiltinInstance}
    };

    public class Print : IBuiltin
    {
        public static Lazy<IBuiltin> BuiltinInstance { get => printInstance; }
        public int ParametersNumber { get => 1; }
        public IBuiltin.Handler BuiltinHandler { get => PrintHandler; }
        private readonly static Lazy<IBuiltin> printInstance = new Lazy<IBuiltin>(() => new Print());
        private Print()
        {
        }

        private object PrintHandler(object[] args)
        {
            try
            {
                Console.Write(args[0]);
            }
            catch
            {
                return 0;
            }
            return 1;
        }
    }

    public class Input : IBuiltin
    {
        public static Lazy<IBuiltin> BuiltinInstance { get => inputInstance; }
        public int ParametersNumber { get => 0; }
        public IBuiltin.Handler BuiltinHandler { get => InputHandler; }
        private readonly static Lazy<IBuiltin> inputInstance = new Lazy<IBuiltin>(() => new Input());
        private Input()
        {
        }

        private object InputHandler(object[] args)
        {
            return Console.ReadLine() ?? "";
        }
    }
}